=begin rdoc
The eventSet represents the event trace collected during 
testing of a Wireless Sensor Network [1]. The eventSet class
thus provides operators for WSN analysis.

Author::    Matthias Woehrle  (mailto:matthias.woehrle@tik.ee.ethz.ch)
Copyright:: Copyright (c) 2008 ETH Zurich
License::   

TODO:

MISSING:

=end

$:.unshift File.dirname(__FILE__)

require 'Event'

class EventSet < Array
  
  #Initializes the event set by calling the constructor of Set, with the given arguments.
  #(Adds processed flags, which is used for set operations.)
  def initialize(*args)
    @processed = false
    #TODO: check that all elements of the set are events
    #how does the constructor work anyway? explicit definition
    #or the initialize is automagically called when reading from the log
    super(*args)
  end
       
 #add method verifies that added object is an instance of event
 #add calls method of super class: Set.
  def add(newevent)
    #checking is expensive ! Leave the check out
    #This cost a lot of cycles, since on every add you check the whole array
    #Rather check at the end
    self << newevent #unless self.include?(newevent)
  end
  
  def merge(eventlist)
    self.concat(eventlist)
  end
  
  def union(eventset)
    self.merge(eventset)
  end  
  
  def intersection(eventset)
    resultset = EventSet.new
    self.each do |event|
      resultset << event if eventset.include?(event)
    end
    return resultset
  end
  
  def subtract(eventset)
    return self - eventset
  end
  #prints the event to a string: Use the to_s function of the event and
  #each event is printed on a new line
   def to_s
    string = "<Eventset = {"
    if self.length > 0
      self.each {|event| string += event.to_s + ","}
      string.chop!
    end
    string += "} /Eventset>\n"
  end
    
  #Compares self to another object.
  #Uses fast check for comparing for Nil and the length.
  #Else each event needs to be checked for equality.
  def ==(other)
    return other.nil? unless self
    return self.nil? unless other
    return false unless self.length == other.length
    selfcopy = self.dup
    comparecopy = other.dup
    match = false
    #Nil is false, so return not other if other is false
    selfcopy.each do |event1|
      match = false
      comparecopy.each  do |event2|
        if event1.eql?(event2)
          comparecopy.delete(event2)
          selfcopy.delete(event2)
          match = true
          puts 'match' if $DEBUG
          next
        end
      end
      return false unless match
    end
    return true
  end

  #Checks that other is an event set.
  #Else checks event sets for equality.
  def eql?(other)
    if other.kind_of?(EventSet) == true
       return self == other
     else
       return false
     end
  end
  

  
  #Hash access to the event Set. Options:
  #1) If parameter is a hash it returns the events, which have a matching value for the given key
  #2) If parameter is a symbol, it returns all events that include the given key
  #3) If parameter is something else an exception is raised
  def [](filter)
    temp = EventSet.new
    if filter.instance_of?Hash
      filter.each do |key,value|
        temp = self.collect { |event| event if event[key] == value }
      end
    elsif filter.instance_of?Fixnum
      return super(filter)
    elsif filter.instance_of?Symbol
        temp = self.collect { |event| event if event.has_key?(filter)}
    else 
      raise "Exception: filter in method Evenset::[](filter) is not of known type but #{filter.class}"
    end
    return EventSet.new(temp.compact)
  end

  def sort_by(&block)
    returnarray = super(&block)
    return EventSet.new(returnarray)
  end
  
  def values(filter_symbol)
    if filter_symbol.kind_of?Symbol
        puts "symbol is ee|#{filter_symbol}|ee" if $DEBUG
        returnArray = Array.new
        temp = self.collect { |event| event if event.has_key?(filter_symbol)}
        temp.compact!
        if temp
          temp.each {|eventitem| returnArray << eventitem[filter_symbol] unless returnArray.include?(eventitem[filter_symbol])}
        end
        return returnArray.sort
    else 
      raise Exception
    end
  end
  
  #Needs a hash as an input or else raises an exception.
  #From each of the events, a key is added to each event with the given value
  def add_key(hash)
    #less checking, assuming user is a good citizen
#    raise Exception unless hash.instance_of?Hash
    self.each do  |event|
       hash.keys.each {|key| event[key] = hash[key]}
    end
  end

  #Needs a symobl entry or else raises an exception.
  #From each of the events, the key given by the symbol is removed
  def remove_key(symbol)
#less checking, assuming user is a good citizen    
#    raise Exception unless symbol.instance_of?Symbol
    self.each do  |event|
       event.delete(symbol)
    end
  end
  
  #use deep_clone function provided for arrays  
  def deep_clone
    super(EventSet.new)
  end
  

end

class Array
  #deep copy of event set by iteration over events
  #some hints from http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/123291
  def deep_clone(returnarray=nil)
    returnarray || returnarray = Array.new()
    self.each do |x|
    array_element = case
        when x.nil?
            nil
        when x.respond_to?(:deep_clone)
            x.deep_clone
        #Fixum and float respond to dup but actually crash on using it
        when (x.respond_to?(:dup) and not x.kind_of?(Fixnum) and not x.kind_of?(Float))
            x.dup
        else
            x
    end
      returnarray << array_element
    end
    return returnarray
  end  
  
  def to_s
    string = ""
        self.each do |x|
          if x.instance_of?(Array) or x.instance_of?(EventSet)
            x.each {|y| string += "#{y.to_s} \n"}
          else
            string += "#{self.index(x)} : #{x.to_s} \n"
          end
        end
  end
end

